// Package web
// Created by GoLand
// @User: lenora
// @Date: 2023/11/27
// @Time: 14:46

package web

import (
	"camp/microbook/internal/domain"
	"camp/microbook/internal/service"
	ijwt "camp/microbook/internal/web/jwt"
	"camp/microbook/pkg/ginx"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
	"regexp"
	"unicode/utf8"
)

const (
	passwordRegexPattern = `^[a-zA-Z]\w{5,17}$`
	birthRegexPattern    = `^((?:19[2-9]\d{1})|(?:20(?:(?:0[0-9])|(?:1[0-9])|(?:2[0-3]))))\-((?:0?[1-9])|(?:1[0-2]))\-((?:0?[1-9])|(?:[1-2][0-9])|30|31)$`
)

type UserHandler struct {
	phoneRexExp    *regexp.Regexp
	passwordRexExp *regexp.Regexp
	birthRexExp    *regexp.Regexp
	svc            service.UserService
	codeSvc        service.CodeService
	jwt            ijwt.Handler
}

func NewUserHandler(svc service.UserService, codeSvc service.CodeService, handler ijwt.Handler) *UserHandler {
	return &UserHandler{
		phoneRexExp:    regexp.MustCompile(phoneRegexPattern),
		passwordRexExp: regexp.MustCompile(passwordRegexPattern),
		birthRexExp:    regexp.MustCompile(birthRegexPattern),
		svc:            svc,
		codeSvc:        codeSvc,
		jwt:            handler,
	}
}

func (h *UserHandler) RegisterRoutes(engine *gin.Engine) {
	uNone := engine.Group("/users/")
	//uNone.POST("signup", h.SignUP)
	uNone.POST("signup", ginx.WrapBody(h.SignUP))
	uNone.POST("login", h.Login)
	uNone.POST("login_sms", h.LoginSMS)
	uNone.GET("profile", ginx.WrapClaims(h.Profile))
	uNone.POST("edit", ginx.WrapBodyAndClaims(h.Edit))
}

func (h *UserHandler) Edit(ctx *gin.Context, req EditUserReq, claims ijwt.UserClaims) (ginx.ApiResponse, error) {

	if req.Nickname != "" && utf8.RuneCountInString(req.Nickname) >= 50 {
		return WrongParamResponse, fmt.Errorf("desc is too long")
	}

	if req.Birth != "" {
		//time.Parse("2006-01-02", req.Birth)
		if isBirth := h.birthRexExp.Match([]byte(req.Birth)); !isBirth {
			return WrongParamResponse, fmt.Errorf("wrong birth")
		}
	}
	if req.Desc != "" && utf8.RuneCountInString(req.Desc) > 200 {
		return WrongParamResponse, fmt.Errorf("desc is too long")
	}

	err := h.svc.Edit(ctx.Request.Context(), claims.Id, req.Nickname, req.Birth, req.Desc)
	switch err {
	case nil:
		return SuccessResp(nil), nil
	case service.ErrUserNotFound:
		return ErrorResp(NOT_FOUND, "用户不存在"), fmt.Errorf("用户不存在")
	default:
		return InternalErrResp(err.Error()), err
	}
}

func (h *UserHandler) Login(ctx *gin.Context) {
	type loginReq struct {
		UserName string `json:"userName" binding:"required"`
		Password string `json:"password" binding:"required"`
	}

	var req loginReq
	if err := ctx.Bind(&req); err != nil {
		webResp.InvalidParametersError(ctx)
		return
	}

	u, err := h.svc.Login(ctx.Request.Context(), req.UserName, req.Password)
	switch err {
	case nil:
		sid := uuid.New().String()
		//使用jwt token
		_ = h.jwt.SetJWTToken(ctx, u.Id, sid)
		////使用session-id
		////设置session
		//sess := sessions.Default(ctx)
		//sess.Set(middlewares.SessionKey, u.Id)
		//sess.Options(sessions.Options{
		//	//session有效期(秒)
		//	MaxAge: 60 * 10,
		//})
		//if err := sess.Save(); err != nil {
		//	ctx.JSON(http.StatusOK, "login error:"+err.Error())
		//	return
		//}
		webResp.ResponseSuccess(ctx, nil)
	case service.ErrInvalidUser:
		webResp.InternalServiceError(ctx, "账号或密码错误")
	default:
		webResp.InternalServiceError(ctx, "login error:"+err.Error())
	}
	return

}

func (h *UserHandler) LoginSMS(ctx *gin.Context) {
	type loginReq struct {
		Phone string `json:"phone" binding:"required"`
		Code  string `json:"code" binding:"required"`
	}

	var req loginReq
	//调用bind方法的时候,如果bind失败的话,bind方法会直接去写相应,返回400
	if err := ctx.Bind(&req); err != nil {
		webResp.InvalidParametersError(ctx)
		return
	}

	if isMatch := h.phoneRexExp.Match([]byte(req.Phone)); !isMatch {
		webResp.InvalidParametersError(ctx)
		return
	}

	err := h.codeSvc.Verify(ctx.Request.Context(), codeGroup[1], req.Phone, req.Code)
	if err != nil {
		//zap包打印日志
		zap.L().Error("verify code failed", zap.Error(err))
		webResp.InternalServiceError(ctx, err.Error())
		return
	}
	u, err := h.svc.FindOrCreate(ctx.Request.Context(), req.Phone)

	switch err {
	case nil:
		//使用jwt token
		ssid := uuid.New().String()
		_ = h.jwt.SetJWTToken(ctx, u.Id, ssid)
		webResp.ResponseSuccess(ctx, u)
	default:
		webResp.InternalServiceError(ctx, "login error:"+err.Error())
	}
	return

}

func (h *UserHandler) Logout(ctx *gin.Context) {
	////删除session信息
	//sess := sessions.Default(ctx)
	//sess.Options(sessions.Options{MaxAge: -1})
	//sess.Save()

	//清除header中的token信息
	err := h.jwt.ClearJWTToken(ctx)
	if err != nil {
		webResp.InternalServiceError(ctx)
		return
	}

	webResp.ResponseSuccess(ctx, nil)
	return

}

func (h *UserHandler) Profile(ctx *gin.Context, claims ijwt.UserClaims) (ginx.ApiResponse, error) {
	u, err := h.svc.Profile(ctx.Request.Context(), claims.Id)
	switch err {
	case nil:
		return SuccessResp(u), nil
	case service.ErrUserNotFound:
		return ErrorResp(NOT_FOUND, "用户不存在"), fmt.Errorf("用户不存在")
	default:
		return InternalErrResp(err.Error()), err
	}
}

func (h *UserHandler) SignUP(ctx *gin.Context, req SignUpReq) (ginx.ApiResponse, error) {
	if req.Password != req.ConfirmPassword {
		return WrongParamResponse, fmt.Errorf("sign up failed")
	}

	isPwd := h.passwordRexExp.Match([]byte(req.Password))
	if !isPwd {
		return WrongParamResponse, fmt.Errorf("sign up failed,password not match pattern")
	}

	err := h.svc.Signup(ctx.Request.Context(), domain.UserBase{
		UserName: req.UserName,
		Password: req.Password,
	})

	switch err {
	case nil:
		return SuccessResp(nil), nil
	case service.ErrDuplicateUserName:
		return InternalErrResp("用户冲突"), fmt.Errorf("用户冲突")
	default:
		return InternalErrResp(err.Error()), err
	}
}
